# Lua Metatables tutorial

Metatables sound like some kind of obscure voodoo that allows all sorts of magical behaviours. The purpose of this tutorial is to demistify them in order to gain an understanding of their behaviour and utility.

This text is oriented to Lua 5.1 and LuaJIT, but it will probably be extensible to newer versions

## What is a metatable?

A metatable is nothing more than a table associated to a value or type.

Really, it's that simple. This association can be made with the function `setmetatable()` or `debug.setmetatable()`, and you can only associate one: setting a new metatable will overwrite the previous association. Whether it is associated to a value or to a type depends on the type. Every table and (full) userdata object can have its own metatable, therefore in that case it applies to each value independently. However, when setting the metatable of a number, all numbers will share the same metatable, and the same happens with the string, function, light userdata, thread, boolean and nil types, hence it applies to the type (or to all values of that type, if you prefer). You can query the metatable of any value in order to retrieve its associated metatable, if it has any.

Note that `getmetatable`/`setmetatable` and `debug.getmetatable`/`debug.setmetatable` are not the same thing. The `debug` versions are "raw", they directly operate on the metatables; the normal ones are more restricted. `debug.getmetatable` will always return the metatable of a value; however, `getmetatable` will first check the metatable to see if it has a `__metatable` field, and if so, it will return its value instead of the metatable. On the other hand, `setmetatable` only lets you set the metatable of table values, not of any other type, and only if the `__metatable` field of the metatable is unset. That is, they behave something like this:

    function getmetatable(obj)
      local mt = debug.getmetatable(obj)
      if mt ~= nil and mt.__metatable ~= nil then
        return mt.__metatable
      end
      return mt
    end
    
    function setmetatable(obj, value)
      if type(obj) ~= "table" then
        error("bad argument #1 to 'setmetatable' (table expected, got " .. type(obj) .. ")")
      end
      local mt = debug.getmetatable(obj)
      if mt ~= nil and mt.__metatable ~= nil then
        error("cannot change a protected metatable")
      end
      debug.setmetatable(obj, value)
      return obj
    end

Here's an example of setting and retrieving the metatable of a number:

    local mt = {a = 1, b = 2}
    local n = debug.setmetatable(0, mt)
    print(getmetatable(5).a)

This snippet displays `1`. All values of type `number` share the same metatable, which is the same table we have associated as the metatable of the value `0`, and the field `a` of that table equals `1`.

However, in most cases, we only need to set the metatable of a table, not of any other type. So, here's another example that does that:

    local mt = {a = 1, b = 2}
    local t1 = {}
    local t2 = setmetatable(t1, mt)
    assert(t1 == t2)
    print(t1.a)
    print(getmetatable(t1).a)

Now, `t1` has a metatable associated to it, which is `mt`. The `setmetatable` function returns the first parameter, `t1` in this case, which is why the comparison will be true. The first `print` statement will print `nil` because `t1` has no field called `a` (in fact it has no fields whatsoever), but its metatable is a table that contains the field `a` and it equals `1`, therefore the second `print` statement will print `1`.

## So, what's special about a metatable?

OK, so far we've seen that a metatable is just a table associated to a type or value. If it's that simple, what makes it so special?

What makes it special is that some operations, like addition, comparison, indexing, and others, when performed on certain types, consult the metatable of the operand, checking for certain special keys, before returning a value or giving an error.

For example, you may think that the operator `+` is simply implemented like this (in Lua pseudocode):

    function operator_plus(value1, value2)
      if type(value1) ~= "number" or type(value2) ~= "number" then
        if type(value1) == "number" then
          value1 = value2 -- report the error on the type of value2
        end
        error("attempt to perform arithmetic on a " .. type(value1) .. " value")
      end
      return value1 + value2
    end

But that's not the case. Instead, the metatable of the first value is first checked, and if it contains a field called `__add`, it is taken as a function and called, and the return value is the result of the operation. If this field doesn't exist, the same is done for the second value. Only when both are missing is the error triggered, so it actually behaves like this:

    function operator_plus(value1, value2)
      if type(value1) ~= "number" or type(value2) ~= "number" then
        local mt = getmetatable(value1)
        if mt ~= nil and mt.__add ~= nil then
          return mt.__add(value1, value2)
        end
        mt = getmetatable(value2)
        if mt ~= nil and mt.__add ~= nil then
          return mt.__add(value1, value2)
        end
        if type(value1) == "number" then
          value1 = value2 -- report the error on the type of value2
        end
        error("attempt to perform arithmetic on a " .. type(value1) .. " value")
      end
      return value1 + value2
    end

In general, except for indexing, normal operators can't be redefined. For example, for the number type, the numeric operations on numbers can't be redefined; you can't make e.g. `1 + 1` return 3. But if it would otherwise raise an error, the metamethod will be called. Here are examples for number and string with `__add` and `__concat`:

    debug.setmetatable(1, {__add = function () print("__add on numbers") end,
      __concat = function () print("__concat on numbers") end})
    getmetatable("").__add = function () print("__add on strings") end
    getmetatable("").__concat = function () print("__concat on strings") end
    if 1 + 1 == 2 then print("ok") end -- prints "ok"
    if 1 .. 1 == "11" then print("ok") end -- prints "ok"
    if 1 .. {} then error() end -- prints "__concat on numbers"
    if 1 + "" then error() end -- prints "__add on numbers"
    if "" + 1 then error() end -- prints "__add on strings"
    if "a" .. "a" == "aa" then print("ok") end -- prints "ok"
    if "a" .. {} then error() end -- prints "__concat on strings"
    if print + 1 then error() end -- prints "__add on numbers"

The last one happens because we haven't defined a metatable for functions, therefore the `__add` method of the metatable for the second argument is checked.

The operation of indexing a table checks the `__index` field of the metatable when the field does not exist, and the operation of creating a new field in a table checks the `__newindex` field of the metatable. However, the case of `__index` and `__newindex` is special, in that instead of a function, the value of the field can be a table. When it is a function, `__index` accepts two parameters: the table and the field, while `__newindex` accepts three: the table, the field and the value. When set to a table, this code:

    mt.__index = LookupTable
    setmetatable(OriginalTable, mt)

behaves as if we had written this:

    mt.__index = function (tbl, key)
      return LookupTable[key]
    end
    setmetatable(OriginalTable, mt)

therefore, it's a shortcut for the very common operation of checking another table when a certain field does not exist in the original table. This access can in turn trigger other metatables. This is commonly used with OriginalTable being an instance or a class, and LookupTable being a class.

Similarly, this code:

    metatable.__newindex = MyTable

behaves like this one:

    metatable.__newindex = function (tbl, key, value)
      MyTable[key] = value
    end

which basically redirects the creation of new fields in a certain table to a different table. This isn't so useful for OOP as the `__index` property; however, you may find other uses for it.

When writing functions for the `__index` or `__newindex` metamethods, it's possible that we run into a circular loop, in which accessing a field triggers the metamethod in turn. To avoid that, we have the global Lua functions `rawget` and `rawset`. `rawget` will read, and `rawset` will set, a field in a table without checking its metatable.

Here's an example of using `__index` for OOP. This example will print the number 5:

    -- Define a table to use as a class
    local MyClass = {}
    MyClass.mt = {__index = MyClass}
    
    function MyClass:printX()
      print(self.x)
    end
    
    local MyInstance = setmetatable({x = 5}, MyClass.mt)
    MyInstance:printX()  -- prints 5

How does that work? When we ask Lua to call the `printX()` method, Lua first tries to access field `printX` in table `MyInstance`, and since it notices that this field isn't present in this table, it checks the metatable of `MyInstance` for an `__index` metamethod. The value of `__index` is `MyClass`, therefore Lua checks for the field `printX` in the table `MyClass`. Since it exists, Lua does not continue checking the metatable of `MyClass`, and instead uses the value of `MyClass.printX`, which is a function. Then it calls that function with the original parameter, which was the table `MyInstance`. The end result is that this call:

    MyInstance:printX()

is interpreted as if it was written like this:

    MyClass.printX(MyInstance)

which prints 5.
